﻿/**
 *@description:
 *@file: compute_test.cpp
 *@author: snow-tyan
 *@email: zziywang@163.com
 *@date: 23 February 2023
 *@copyright: 2018-2022 AiRiA. All rights reserved.
 */

#include "mygl/shader.h"
#include "mygl/compute_shader.h"
#include "mygl/util.h"
#include "mygl/resource_manager.h"

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

#ifndef PROJECT_ROOT
#define PROJECT_ROOT "F:/VisualStudioProjects/my-open-gl"
#endif

using namespace mygl;

const std::string kShaderRoot =
    PROJECT_ROOT + std::string("/07-InPractice/05-ComputeShaderTest/");
const unsigned int kWindowWidth = 800;
const unsigned int kWindowHeight = 600;
const char* kWindowName = "ComputeShaderTest";

void processInput(GLFWwindow* window);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
GLuint InitRenderData();

int main() {
  glfwInit();
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  // glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
  GLFWwindow* window =
      glfwCreateWindow(kWindowWidth, kWindowHeight, kWindowName, nullptr, nullptr);
  glfwMakeContextCurrent(window);
  glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

  // glad: load all OpenGL function pointers
  if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
  }
  // init shader
  auto shader = ResourceManager::LoadShader("test", kShaderRoot + "shader.vert",
                                            kShaderRoot + "shader.frag");
  auto compute_shader =
      ResourceManager::LoadComputeShader("img_st", kShaderRoot + "shader.comp");

  // texture size
  const GLuint kTextureWidth = 512, kTextureHeight = 512;
  GLuint texture;

  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, kTextureWidth, kTextureHeight, 0, GL_RGBA,
               GL_FLOAT, nullptr);

  glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);

  compute_shader->UseProgram();
  // num_group_x, num_group_y, num_group_z
  glDispatchCompute(kTextureWidth, kTextureHeight, 1);
  // make sure 数据写入完成
  glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

  GLuint vao = InitRenderData();
  glm::mat4 projection =
      glm::ortho(0.0f, float(kWindowWidth), float(kWindowHeight), 0.0f, -1.0f, 1.0f);
  // transfrom
  glm::mat4 model(1.0f);
  model = glm::translate(model, glm::vec3(0.0f));
  // 不做旋转，仅做位移和缩放
  model = glm::scale(model, glm::vec3(kWindowWidth, kWindowHeight, 1.0f));
  shader->UseProgram();
  shader->SetUniformMat4Float("projection", projection);
  shader->SetUniformMat4Float("model", model);
  shader->SetUniformInt("image", 0);

  while (!glfwWindowShouldClose(window)) {
    processInput(window);

    glClearColor(0.1, 0.1, 0.1, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);

    glfwSwapBuffers(window);
    glfwPollEvents();
  }
  mygl::ResourceManager::Clear();
  glDeleteVertexArrays(1, &vao);
  glfwTerminate();
  return 0;
}

void processInput(GLFWwindow* window) {
  if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
    glfwSetWindowShouldClose(window, true);
  }
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
  glViewport(0, 0, width, height);
}

GLuint InitRenderData() {
  GLfloat vertices[] = {
      1.0f, 1.0f, 1.0f, 1.0f,  // 右上 0
      1.0f, 0.0f, 1.0f, 0.0f,  // 右下 1
      0.0f, 0.0f, 0.0f, 0.0f,  // 左下 2
      0.0f, 1.0f, 0.0f, 1.0f,  // 左上 3
  };
  GLshort indices[] = {
      0, 1, 3,  //
      1, 2, 3,
  };
  GLuint vao, vbo, ebo;
  glGenVertexArrays(1, &vao);
  glGenBuffers(1, &vbo);
  glGenBuffers(1, &ebo);

  glBindVertexArray(vao);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

  glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void*)0);
  glEnableVertexAttribArray(0);
  // Unbind
  // 必须先Unbind VAO 否则会崩 因为VAO中存很多属性和索引数组
  glBindVertexArray(GL_NONE);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_NONE);
  glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
  return vao;
}